Commit | Line | Data |
---|---|---|
2aff8b5c | 1 | <?xml version="1.0" encoding="utf-8" ?> |
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | |
3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
4 | <html xmlns="http://www.w3.org/1999/xhtml"> | |
5 | <head> | |
6 | <title>Metaobject Protocols</title> | |
7 | <meta name="generator" content="muse.el" /> | |
8 | <meta http-equiv="Content-Type" | |
9 | content="text/html; charset=utf-8" /> | |
11f9bd69 CE |
10 | <meta name="viewport" |
11 | content="width=device-width, initial-scale=1.0" /> | |
98266870 | 12 | <link href="https://feeds.unknownlamer.org/rss/site-updates" |
a7e21d41 | 13 | rel="alternate" type="application/rss+xml" title="Updates Feed" /> |
14 | ||
7404d4e1 | 15 | <link rel="stylesheet" href="default.css" /> |
2aff8b5c | 16 | </head> |
17 | <body> | |
18 | <h1>Metaobject Protocols</h1> | |
19 | <div class="contents"> | |
20 | <dl> | |
21 | <dt> | |
22 | <a href="#sec1">Background</a> | |
23 | </dt> | |
24 | <dd> | |
25 | <dl> | |
26 | <dt> | |
27 | <a href="#sec2">Object Protocols</a> | |
28 | </dt> | |
29 | <dt> | |
30 | <a href="#sec3">CLOS Way of OO</a> | |
31 | </dt> | |
32 | <dd> | |
33 | <dl> | |
34 | <dt> | |
a7e21d41 | 35 | <a href="#sec4">Classes for Scratch Data and Types</a> |
2aff8b5c | 36 | </dt> |
37 | <dt> | |
a7e21d41 | 38 | <a href="#sec5">Generics with Methods that Implement Protocols</a> |
2aff8b5c | 39 | </dt> |
40 | </dl> | |
41 | </dd> | |
42 | </dl> | |
43 | </dd> | |
44 | <dt> | |
45 | <a href="#sec6">Limitations of Default Language Behavior</a> | |
46 | </dt> | |
47 | <dd> | |
48 | <dl> | |
49 | <dt> | |
50 | <a href="#sec7">Slot Storage</a> | |
51 | </dt> | |
52 | <dt> | |
53 | <a href="#sec8">Design Patterns</a> | |
54 | </dt> | |
55 | </dl> | |
56 | </dd> | |
57 | <dt> | |
58 | <a href="#sec9">Metasoftware</a> | |
59 | </dt> | |
60 | <dd> | |
61 | <dl> | |
62 | <dt> | |
63 | <a href="#sec10">Runtime Generated Classes</a> | |
64 | </dt> | |
65 | <dt> | |
66 | <a href="#sec11">Object Inspection</a> | |
67 | </dt> | |
68 | </dl> | |
69 | </dd> | |
70 | <dt> | |
71 | <a href="#sec12">Metaobject Protocols</a> | |
72 | </dt> | |
73 | <dd> | |
74 | <dl> | |
75 | <dt> | |
76 | <a href="#sec13">Limited/Generalized Internals of the Implementation</a> | |
77 | </dt> | |
78 | <dt> | |
79 | <a href="#sec14">Classes of MOPs</a> | |
80 | </dt> | |
81 | <dd> | |
82 | <dl> | |
83 | <dt> | |
84 | <a href="#sec15">Reflective</a> | |
85 | </dt> | |
86 | <dt> | |
87 | <a href="#sec16">Intercessory</a> | |
88 | </dt> | |
89 | </dl> | |
90 | </dd> | |
91 | <dt> | |
92 | <a href="#sec17">Violation of Encapsulation?</a> | |
93 | </dt> | |
94 | </dl> | |
95 | </dd> | |
96 | <dt> | |
97 | <a href="#sec18">MOP Design Principles</a> | |
98 | </dt> | |
99 | <dd> | |
100 | <dl> | |
101 | <dt> | |
102 | <a href="#sec19">Layered Protocol</a> | |
103 | </dt> | |
104 | <dd> | |
105 | <dl> | |
106 | <dt> | |
a7e21d41 | 107 | <a href="#sec20">Top Level <strong>Must</strong> Call Lower Level Methods</a> |
2aff8b5c | 108 | </dt> |
109 | <dt> | |
a7e21d41 | 110 | <a href="#sec21">Lower Level Methods are Easier to Customize</a> |
2aff8b5c | 111 | </dt> |
112 | </dl> | |
113 | </dd> | |
114 | <dt> | |
115 | <a href="#sec22">Functional Where Possible</a> | |
116 | </dt> | |
117 | <dd> | |
118 | <dl> | |
119 | <dt> | |
120 | <a href="#sec23">Memoization</a> | |
121 | </dt> | |
122 | <dt> | |
a7e21d41 | 123 | <a href="#sec24">Constant Shared Return Values</a> |
2aff8b5c | 124 | </dt> |
125 | </dl> | |
126 | </dd> | |
127 | <dt> | |
36fbff92 | 128 | <a href="#sec25">Procedural Only Where Necessary</a> |
2aff8b5c | 129 | </dt> |
2aff8b5c | 130 | <dt> |
a7e21d41 | 131 | <a href="#sec26">Real World</a> |
2aff8b5c | 132 | </dt> |
133 | <dd> | |
134 | <dl> | |
135 | <dt> | |
a7e21d41 | 136 | <a href="#sec27">UCW and Arnesi</a> |
2aff8b5c | 137 | </dt> |
138 | <dt> | |
a7e21d41 | 139 | <a href="#sec28">CLSQL</a> |
2aff8b5c | 140 | </dt> |
141 | <dt> | |
a7e21d41 | 142 | <a href="#sec29">Elephant</a> |
2aff8b5c | 143 | </dt> |
144 | </dl> | |
145 | </dd> | |
146 | </dl> | |
147 | </dd> | |
148 | <dt> | |
36fbff92 | 149 | <a href="#sec30">Sources and Further Reading</a> |
2aff8b5c | 150 | </dt> |
151 | <dd> | |
152 | <dl> | |
153 | <dt> | |
a7e21d41 | 154 | <a href="#sec31">Sources</a> |
2aff8b5c | 155 | </dt> |
156 | <dd> | |
157 | <dl> | |
158 | <dt> | |
a7e21d41 | 159 | <a href="#sec32">The Art of the Metaobject Protocol</a> |
2aff8b5c | 160 | </dt> |
161 | <dt> | |
a7e21d41 | 162 | <a href="#sec33">CLOS MOP Specification</a> |
2aff8b5c | 163 | </dt> |
164 | <dt> | |
a7e21d41 | 165 | <a href="#sec34">Metaobject Protocols: Why We Want Them and What Else They Can Do</a> |
2aff8b5c | 166 | </dt> |
167 | <dt> | |
a7e21d41 | 168 | <a href="#sec35">Why Are Black Boxes so Hard to Reuse?</a> |
2aff8b5c | 169 | </dt> |
170 | </dl> | |
171 | </dd> | |
172 | <dt> | |
a7e21d41 | 173 | <a href="#sec36">Further Reading</a> |
2aff8b5c | 174 | </dt> |
175 | <dd> | |
176 | <dl> | |
177 | <dt> | |
a7e21d41 | 178 | <a href="#sec37">A Metaobject Protocol for C++</a> |
2aff8b5c | 179 | </dt> |
180 | <dt> | |
a7e21d41 | 181 | <a href="#sec38">Open Implementations and Metaobject Protocols</a> |
2aff8b5c | 182 | </dt> |
183 | </dl> | |
184 | </dd> | |
36fbff92 | 185 | <dt> |
186 | <a href="#sec39">Software</a> | |
187 | </dt> | |
188 | <dd> | |
189 | <dl> | |
190 | <dt> | |
191 | <a href="#sec40">Closer to MOP</a> | |
192 | </dt> | |
193 | </dl> | |
194 | </dd> | |
2aff8b5c | 195 | </dl> |
196 | </dd> | |
197 | </dl> | |
198 | </div> | |
199 | ||
200 | ||
11f9bd69 CE |
201 | <!-- Page published by Emacs Muse begins here --> |
202 | <p>In Fall of 2006 I did a small project on Metaobject Protocols for my | |
2aff8b5c | 203 | CS 331 class. Here lie my notes which may perhaps be useful to |
204 | others. I hope to expand them into something more useful over time.</p> | |
205 | ||
206 | <h2><a name="sec1" id="sec1"></a> | |
207 | Background</h2> | |
208 | ||
209 | <h3><a name="sec2" id="sec2"></a> | |
210 | Object Protocols</h3> | |
211 | ||
212 | <p class="first">An object protocol is a set of methods and specification of the | |
213 | interactions between the methods which provide some generic behavior | |
214 | (e.g. of a sequence) that are then implemented by classes which | |
215 | conform to the protocol (e.g. a vector or list). In most object | |
216 | systems a class contains both the methods which implement a protocol | |
217 | and the data used by the implementation. The intent is to emulate | |
218 | state machines which pass messages between each other.</p> | |
219 | ||
220 | ||
221 | <h3><a name="sec3" id="sec3"></a> | |
222 | CLOS Way of OO</h3> | |
223 | ||
224 | <p class="first">The Common Lisp Object System (CLOS) is different. It separates | |
225 | the data and method concepts into classes and generics. A class | |
226 | contains data fields only, and a generic has methods specialized for | |
227 | certain types attached to it. This seems a bit weird at first, but is | |
228 | significantly more powerful as it encourages complete encapsulation | |
229 | through its use of classes primarily for method specialization rather | |
230 | than for state storage.</p> | |
231 | ||
2aff8b5c | 232 | <h4><a name="sec4" id="sec4"></a> |
a7e21d41 | 233 | Classes for Scratch Data and Types</h4> |
2aff8b5c | 234 | |
235 | <p class="first">In CLOS classes store data in slots (which are the same as data | |
236 | members). Encapsulation is not provided; any bit of code can use | |
237 | <code>slot-value</code> to access or set the value of a slot. This may seem odd at | |
238 | first, but encapsulation is of questionable importance as the slots | |
239 | are meant only to be used by the protocol defined around the class.</p> | |
240 | ||
a7e21d41 | 241 | <p>Classes are defined with <code>defclass</code></p> |
2aff8b5c | 242 | |
243 | <pre class="src"> | |
7404d4e1 | 244 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">name</span> (superclasses ...) |
245 | ((slot-name <span class="emacs-face-builtin">:accessor</span> slot-accessor ...) | |
2aff8b5c | 246 | ...) |
247 | (class-options ...)) | |
248 | ||
7404d4e1 | 249 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">example</span> () |
250 | ((foo <span class="emacs-face-builtin">:accessor</span> foo-of <span class="emacs-face-builtin">:initform</span> 5))) | |
2aff8b5c | 251 | |
7404d4e1 | 252 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">example-child</span> (example) |
253 | ((bar <span class="emacs-face-builtin">:accessor</span> bar-of <span class="emacs-face-builtin">:initform</span> (list 1 2 3)))) | |
2aff8b5c | 254 | </pre> |
255 | ||
36fbff92 | 256 | <p>Slot definitions have several options; the above example shows only the |
2aff8b5c | 257 | <code>:accessor</code> and <code>:initform</code> options which are the most commonly |
258 | used. <code>:accessor</code> generates an accessor for the slot (e.g. if you have | |
a7e21d41 | 259 | an instance of <code>example</code> you can <code>(setf (foo-of some-example-instance) |
260 | 'some-value)</code> to set and <code>(foo-of some-example-instance)</code> to access the | |
2aff8b5c | 261 | value). <code>:initform</code> provides a default initial value for the slot as a |
a7e21d41 | 262 | symbolic expression to be evaluated when an instance is created in the |
263 | lexical environment of the class definition.</p> | |
2aff8b5c | 264 | |
265 | ||
266 | <h4><a name="sec5" id="sec5"></a> | |
a7e21d41 | 267 | Generics with Methods that Implement Protocols</h4> |
2aff8b5c | 268 | |
269 | <p class="first">Generics are like normal functions in Lisp, but they only provide a | |
270 | lambda list (parameter list). Methods are added to the generic which | |
a7e21d41 | 271 | specialize on the types of their parameters and provide an |
272 | implementation. This allows writing rich layered protocols which can | |
273 | enable selective modification of individual facets with minimal code.</p> | |
2aff8b5c | 274 | |
275 | <pre class="src"> | |
7404d4e1 | 276 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">generic</span> (parameters ...) |
2aff8b5c | 277 | (options) ...) |
278 | ||
7404d4e1 | 279 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">generic-name</span> ((parameter type) parameter ...) |
4222507d | 280 | <span class="emacs-face-doc">"documentation string"</span> |
2aff8b5c | 281 | body) |
282 | ||
7404d4e1 | 283 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">foo</span> (bar baz quux) |
4222507d | 284 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Process the baz with the quux capacitor to make the |
2aff8b5c | 285 | foo widget fly into the sky at warp speed"</span>)) |
286 | ||
7404d4e1 | 287 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">foo</span> ((bar example) baz (quux capacitor)) |
2aff8b5c | 288 | (launch bar (process-with quux baz))) |
289 | </pre> | |
290 | ||
291 | <p>A method lambda list differs from a normal lambda list only in that it | |
292 | can specify the type of the parameter using the notation <code>(name type)</code>. | |
293 | Note also that methods can specialize on the types of every | |
294 | argument and not just the first one. This is quite powerful for | |
295 | reasons outside of the scope of this presentation.</p> | |
296 | ||
297 | ||
298 | ||
299 | ||
300 | <h2><a name="sec6" id="sec6"></a> | |
301 | Limitations of Default Language Behavior</h2> | |
302 | ||
303 | <p class="first">The behavior of a language is a compromise between many competing | |
a7e21d41 | 304 | issues that attempts to be as generally useful as possible so that |
305 | <em>most</em> applications will have no issue with the default behavior. There | |
306 | are, however, certain applications that could be cleanly written with | |
307 | minor modifications to the behavior of the language, but would be | |
308 | impossible or quite difficult to write otherwise.</p> | |
2aff8b5c | 309 | |
310 | <h3><a name="sec7" id="sec7"></a> | |
311 | Slot Storage</h3> | |
312 | ||
313 | <p class="first">Most languages choose to preallocate storage for all of the slots of | |
a7e21d41 | 314 | an instance. Now imagine a contact database that stores information |
315 | about people in slots of a class. There may be dozens of slots, but | |
316 | often many of them will be left blank. If slot storage is preallocated | |
317 | much memory will be wasted and the database may not be able to fit | |
318 | into the memory of the hardware it must run on (perhaps for financial | |
319 | reasons, huge datasets, etc.).</p> | |
2aff8b5c | 320 | |
321 | <p>To save memory the author of the contact database must implement his | |
322 | own system to store properties and allocate them lazily. This | |
323 | represents a fair bit of effort, and would implement a system that | |
a7e21d41 | 324 | differed from the existing slot system of classes only regarding slot |
325 | storage.</p> | |
2aff8b5c | 326 | |
a7e21d41 | 327 | <p>It would be useful if there were a way to customize slot allocation in |
328 | instances. The customizations would be minor and require overriding | |
2aff8b5c | 329 | only the initial allocation behavior and the behavior of the first |
330 | assignment to the slot. It is a a trivial problem in a language that | |
a7e21d41 | 331 | allows customization of these behaviors.</p> |
2aff8b5c | 332 | |
333 | ||
334 | <h3><a name="sec8" id="sec8"></a> | |
335 | Design Patterns</h3> | |
336 | ||
337 | <p class="first">Design Patterns are generalized versions of common patterns found in | |
338 | programs. Many of them are merely methods to get around deficiencies | |
339 | in the language, and can be quite messy to implement in some | |
a7e21d41 | 340 | languages. Ideally a pattern would be subsumed by the language, but |
36fbff92 | 341 | real world constraints require language standards to remain fairly |
a7e21d41 | 342 | static.</p> |
2aff8b5c | 343 | |
344 | ||
345 | ||
346 | <h2><a name="sec9" id="sec9"></a> | |
347 | Metasoftware</h2> | |
348 | ||
349 | <p class="first">Some types of programs could be written easily if the language were | |
a7e21d41 | 350 | customizable but are nearly impossible to write when it is not.</p> |
2aff8b5c | 351 | |
352 | <h3><a name="sec10" id="sec10"></a> | |
353 | Runtime Generated Classes</h3> | |
354 | ||
355 | <p class="first">Say you wanted to write a video game where players could create their | |
356 | own objects, attach behaviors to the objects, and perhaps mix | |
357 | different objects together to create new ones. When you abstract the | |
358 | problem this looks just like an object system! Wouldn't it be nice if | |
a7e21d41 | 359 | your program could create new classes and methods on the fly portably?</p> |
2aff8b5c | 360 | |
361 | ||
362 | <h3><a name="sec11" id="sec11"></a> | |
363 | Object Inspection</h3> | |
364 | ||
a7e21d41 | 365 | <p class="first">Imagine you were developing a complicated program with many different |
366 | objects that interacted in fairly complex ways. A tool to inspect the | |
367 | structure of objects while debugging would be quite useful, but in a | |
368 | traditional language would be impossible to implement portably. This | |
369 | could force you to purchase a certain compiler implementation which | |
370 | provided an inspector, and even then would likely not be customizable.</p> | |
2aff8b5c | 371 | |
372 | <p>This problem can be generalized to apply to most debugging tools; it | |
373 | would be useful to write such tools portably because users of the | |
374 | <em>language</em> and not the <em>compiler</em> need to debug software. Sharing | |
375 | infrastructure would result in better tools (more developers), and | |
a7e21d41 | 376 | save the man-years of wasted effort that comes with having to rewrite |
377 | unportable tools from scratch multiple times.</p> | |
2aff8b5c | 378 | |
379 | ||
380 | ||
381 | <h2><a name="sec12" id="sec12"></a> | |
382 | Metaobject Protocols</h2> | |
383 | ||
384 | <h3><a name="sec13" id="sec13"></a> | |
385 | Limited/Generalized Internals of the Implementation</h3> | |
386 | ||
a7e21d41 | 387 | <p class="first">A Metaobject Protocol (MOP) is a generalized and limited subset of the |
388 | underlying language implementation. It is limited to allow multiple | |
389 | implementation strategies; this, along with careful design, is | |
390 | essential because programming language research is ever advancing and | |
391 | new techniques for creating more reliable and faster implementations | |
392 | are still being discovered.</p> | |
2aff8b5c | 393 | |
394 | <p>This subset of the implementation is exported as a set of methods on | |
a7e21d41 | 395 | metaobjects. Thus the language is implemented in itself. The system |
396 | can then be customized using the extension and overriding features of | |
397 | the language itself.</p> | |
2aff8b5c | 398 | |
399 | ||
400 | <h3><a name="sec14" id="sec14"></a> | |
401 | Classes of MOPs</h3> | |
402 | ||
403 | <h4><a name="sec15" id="sec15"></a> | |
404 | Reflective</h4> | |
405 | ||
a7e21d41 | 406 | <p class="first">A reflective MOP provides an interface to information <em>about</em> the |
407 | running system. It exposes class relationships, the methods attached | |
408 | to a generic, etc. A reflective MOP often provides some functionality | |
409 | for creating new classes at runtime. Smalltalk was one of the first | |
410 | languages to expose a reflective MOP.</p> | |
2aff8b5c | 411 | |
412 | <h5>Example: Object Inspector</h5> | |
413 | ||
2aff8b5c | 414 | <pre class="src"> |
7404d4e1 | 415 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">example-inspect</span> (instance) |
4222507d | 416 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Simple object inspector using CLOS MOP"</span>)) |
2aff8b5c | 417 | |
7404d4e1 | 418 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">example-inspect</span> ((instance t)) |
419 | (format t <span class="emacs-face-string">"Simple Object~% Value: ~S~%"</span> instance)) | |
2aff8b5c | 420 | |
7404d4e1 | 421 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">example-inspect</span> ((instance standard-object)) |
422 | (<span class="emacs-face-keyword">let</span> ((class (class-of instance))) | |
423 | (format t <span class="emacs-face-string">"Class: ~S, Superclasses: ~S~%"</span> | |
2aff8b5c | 424 | (class-name class) |
425 | (mapcar #'class-name | |
426 | (class-precedence-list class))) | |
7404d4e1 | 427 | (<span class="emacs-face-keyword">let</span> ((slot-names (mapcar #'slot-definition-name |
2aff8b5c | 428 | (class-slots class)))) |
7404d4e1 | 429 | (format t <span class="emacs-face-string">"Slots: ~%~{ ~S~%~}"</span> slot-names) |
2aff8b5c | 430 | (inspect-loop slot-names instance #'example-inspect)))) |
431 | ||
7404d4e1 | 432 | (<span class="emacs-face-keyword">defun</span> <span class="emacs-face-function-name">inspect-loop</span> (slots instance inspector) |
433 | (format t <span class="emacs-face-string">"Enter slot to inspect or :pop to go up one level: "</span>) | |
2aff8b5c | 434 | (finish-output) |
7404d4e1 | 435 | (<span class="emacs-face-keyword">let*</span> ((slot (read)) |
2aff8b5c | 436 | (found-slot (member slot slots))) |
7404d4e1 | 437 | (<span class="emacs-face-keyword">cond</span> (found-slot |
2aff8b5c | 438 | (funcall inspector (slot-value instance slot)) |
439 | (funcall inspector instance)) | |
7404d4e1 | 440 | ((eq slot <span class="emacs-face-builtin">:pop</span>) t) |
2aff8b5c | 441 | (t |
7404d4e1 | 442 | (format t <span class="emacs-face-string">"~S is invalid. Valid slot names: ~S~%"</span> |
2aff8b5c | 443 | slot |
444 | slots) | |
445 | (inspect-loop slots instance inspector))))) | |
446 | </pre> | |
447 | ||
448 | ||
a7e21d41 | 449 | <h5>Example: Runtime Generated Classes and Methods</h5> |
450 | ||
451 | ||
452 | ||
453 | <h4><a name="sec16" id="sec16"></a> | |
454 | Intercessory</h4> | |
455 | ||
456 | <p class="first">Intercessory MOPs allow the user to customize language behavior by | |
457 | implementing methods which override certain aspects of the language | |
458 | behavior. This class of MOPs are what make MOPs especially | |
459 | powerful. No longer must a problem be restructured to fit the | |
36fbff92 | 460 | implementation language; the underlying language can be reshaped to |
a7e21d41 | 461 | fit the task at hand, and obfuscation of the intended structure of the |
462 | application can be avoided.</p> | |
463 | ||
464 | <h5>Example: Lazily Allocated Slots</h5> | |
465 | ||
466 | ||
467 | <h5>Example: Observer Design Pattern</h5> | |
2aff8b5c | 468 | |
a7e21d41 | 469 | <p>A simple implementation of the observer pattern is under 100 lines, |
2aff8b5c | 470 | and the user level code requires only a single line of code to make |
471 | any existing class observable.</p> | |
472 | ||
473 | <p>In a language lacking a MOP, implementing the observer pattern | |
474 | requires modifying every accessor of a class to explicitly invoke any | |
36fbff92 | 475 | observers, and necessitates the addition of a mixin class to the class |
476 | hierarchy. The fact that an object can be observed is a meta property | |
2aff8b5c | 477 | of the class, and forcing it to be implemented at the application |
36fbff92 | 478 | level dirties the inheritance hierarchy and adds unnecessary meta |
2aff8b5c | 479 | details to the program.</p> |
480 | ||
481 | <pre class="src"> | |
7404d4e1 | 482 | <span class="emacs-face-comment-delimiter">;;; </span><span class="emacs-face-comment">This metaclass adds a slot to instances which use it, and so the |
483 | </span><span class="emacs-face-comment-delimiter">;;; </span><span class="emacs-face-comment">system is defined in its own package to avoid name conflicts | |
484 | </span>(<span class="emacs-face-keyword">defpackage</span> <span class="emacs-face-type">:observer</span> | |
36fbff92 | 485 | (<span class="emacs-face-builtin">:use</span> <span class="emacs-face-builtin">:cl</span> <span class="emacs-face-builtin">:c2mop</span>) |
7404d4e1 | 486 | (<span class="emacs-face-builtin">:export</span> observable register-observer unregister-observer)) |
2aff8b5c | 487 | |
7404d4e1 | 488 | (<span class="emacs-face-keyword">in-package</span> <span class="emacs-face-builtin">:observer</span>) |
2aff8b5c | 489 | |
7404d4e1 | 490 | <span class="emacs-face-comment-delimiter">;;; </span><span class="emacs-face-comment">Metaclass |
491 | </span>(<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">observable</span> (standard-class) | |
2aff8b5c | 492 | () |
4222507d | 493 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Metaclass for observable objects"</span>)) |
2aff8b5c | 494 | |
7404d4e1 | 495 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">compute-slots</span> ((class observable)) |
4222507d | 496 | <span class="emacs-face-doc">"Add a slot for storing observers to observable instances"</span> |
2aff8b5c | 497 | (cons (make-instance 'standard-effective-slot-definition |
7404d4e1 | 498 | <span class="emacs-face-builtin">:name</span> 'observers |
499 | <span class="emacs-face-builtin">:initform</span> '(make-hash-table) | |
500 | <span class="emacs-face-builtin">:initfunction</span> #'(<span class="emacs-face-keyword">lambda</span> () (make-hash-table))) | |
2aff8b5c | 501 | (call-next-method))) |
502 | ||
7404d4e1 | 503 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">validate-superclass</span> ((class observable) |
2aff8b5c | 504 | (super standard-class)) |
505 | t) | |
506 | ||
7404d4e1 | 507 | (<span class="emacs-face-keyword">defun</span> <span class="emacs-face-function-name">register-observer</span> (instance slot-name key closure) |
2aff8b5c | 508 | (register-observer-with-class (class-of instance) |
509 | instance | |
510 | slot-name | |
511 | key | |
512 | closure)) | |
513 | ||
7404d4e1 | 514 | (<span class="emacs-face-keyword">defun</span> <span class="emacs-face-function-name">unregister-observer</span> (instance slot-name key) |
2aff8b5c | 515 | (unregister-observer-with-class (class-of instance) |
516 | instance | |
517 | slot-name | |
518 | key)) | |
519 | ||
7404d4e1 | 520 | (<span class="emacs-face-keyword">defun</span> <span class="emacs-face-function-name">get-observers</span> (instance slot-name) |
2aff8b5c | 521 | (get-observers-with-class (class-of instance) |
522 | instance | |
523 | slot-name)) | |
524 | ||
7404d4e1 | 525 | (<span class="emacs-face-keyword">defun</span> <span class="emacs-face-function-name">add-observer-table</span> (instance slot-name) |
2aff8b5c | 526 | (setf (gethash slot-name (slot-value instance |
527 | 'observers)) | |
528 | (make-hash-table))) | |
529 | ||
7404d4e1 | 530 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">register-observer-with-class</span> (class instance slot-name key closure)) |
531 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">unregister-observer-with-class</span> (class | |
2aff8b5c | 532 | instance |
533 | slot-name | |
534 | key)) | |
535 | ||
7404d4e1 | 536 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">register-observer-with-class</span> ((class observable) |
2aff8b5c | 537 | instance |
538 | slot-name | |
539 | key | |
540 | closure) | |
541 | (setf (gethash key | |
542 | (or (gethash slot-name | |
543 | (slot-value instance 'observers)) | |
7404d4e1 | 544 | <span class="emacs-face-comment-delimiter">;; </span><span class="emacs-face-comment">Lazily add observer hash tables |
2aff8b5c | 545 | </span> (add-observer-table instance slot-name))) |
546 | closure)) | |
547 | ||
7404d4e1 | 548 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">unregister-observer-with-class</span> ((class observable) |
2aff8b5c | 549 | instance |
550 | slot-name | |
551 | key) | |
552 | (remhash key (gethash slot-name | |
553 | (slot-value instance 'observers)))) | |
554 | ||
7404d4e1 | 555 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">get-observers-with-class</span> ((class observable) |
2aff8b5c | 556 | instance |
557 | slot-name) | |
558 | (gethash slot-name (slot-value instance 'observers))) | |
559 | ||
4222507d | 560 | (<span class="emacs-face-keyword">defmethod</span> (<span class="emacs-face-function-name">setf slot-value-using-class</span>) <span class="emacs-face-builtin">:before</span> (new-value |
2aff8b5c | 561 | (class observable) |
562 | instance | |
563 | slot) | |
7404d4e1 | 564 | (<span class="emacs-face-keyword">let</span> ((slot-name (slot-definition-name slot))) |
565 | (<span class="emacs-face-keyword">if</span> (not (eq 'observers slot-name)) | |
566 | (<span class="emacs-face-keyword">let</span> ((observers | |
2aff8b5c | 567 | (get-observers instance (slot-definition-name slot)))) |
7404d4e1 | 568 | (<span class="emacs-face-keyword">if</span> observers |
569 | (maphash #'(<span class="emacs-face-keyword">lambda</span> (key observer) | |
2aff8b5c | 570 | (funcall observer |
7404d4e1 | 571 | (<span class="emacs-face-keyword">if</span> (slot-boundp instance slot-name) |
2aff8b5c | 572 | (slot-value instance slot-name) |
573 | nil) | |
574 | new-value)) | |
575 | observers)))))) | |
576 | </pre> | |
577 | ||
578 | ||
a7e21d41 | 579 | |
580 | ||
581 | ||
582 | <h3><a name="sec17" id="sec17"></a> | |
583 | Violation of Encapsulation?</h3> | |
584 | ||
585 | <p class="first">A MOP may seem like a violation of encapsulation by revealing some | |
586 | implementation details, but in reality a well designed protocol does | |
587 | not reveal anything which was not already exposed. Implementation | |
588 | decisions affect users, and some of these details do leak through to | |
589 | higher levels (e.g. the memory layout of slots). Implicit in the | |
590 | protocol specification are these implementation details, and the MOP | |
591 | merely makes this limited subset available for customization.</p> | |
592 | ||
593 | <p>A MOP makes it possible to customize certain implementation decisions | |
594 | that do not <strong>radically</strong> alter the behavior of the base language. The | |
595 | conceptual vocabulary of the system retains its meaning, and so code | |
596 | written in one dialect can interact with code written in another | |
597 | without knowing that they speak different ones.</p> | |
598 | ||
599 | ||
600 | ||
601 | <h2><a name="sec18" id="sec18"></a> | |
602 | MOP Design Principles</h2> | |
603 | ||
604 | <h3><a name="sec19" id="sec19"></a> | |
605 | Layered Protocol</h3> | |
606 | ||
607 | <p class="first">A layered protocol design is good for both meta and normal object | |
608 | protocols, and enables a combinatorial explosion of customizations to | |
609 | the protocol.</p> | |
610 | ||
611 | <h4><a name="sec20" id="sec20"></a> | |
612 | Top Level <strong>Must</strong> Call Lower Level Methods</h4> | |
613 | ||
614 | <p class="first">The top level methods of a layered protocol are required to call | |
615 | certain lower level methods to perform some tasks. This both makes it | |
616 | easier to customize the top level methods (which perform very broad | |
617 | tasks) by providing some pieces of implementation for the programmer, | |
618 | and enables more customization by opening up the replacement of lower | |
619 | level functions as a way to alter a small detail of the high level | |
620 | behavior.</p> | |
621 | ||
622 | ||
623 | <h4><a name="sec21" id="sec21"></a> | |
624 | Lower Level Methods are Easier to Customize</h4> | |
625 | ||
626 | <p class="first">The lower level methods of a MOP are limited in scope and can be | |
627 | implemented easily. Often the desired changes to language behavior are | |
628 | minor, and having methods that perform simple tasks which are often | |
629 | customized reduces the effort required to extend the system.</p> | |
630 | ||
631 | ||
632 | ||
633 | <h3><a name="sec22" id="sec22"></a> | |
634 | Functional Where Possible</h3> | |
635 | ||
636 | <p class="first">Functional protocols are preferred for MOPs (and object protocols in | |
637 | general). Functional protocols open up several optimizations for the | |
638 | implementation without burdening the user of the protocol.</p> | |
639 | ||
640 | <h4><a name="sec23" id="sec23"></a> | |
641 | Memoization</h4> | |
642 | ||
643 | <p class="first">Memoization is the process of saving the results of a function call | |
644 | for future use. This avoids expensive recomputation of values which | |
645 | have not changed (recall that a true function will always return the | |
646 | same result when given the same arguments).</p> | |
647 | ||
648 | <p>A functional MOP can be optimized easily by exploiting this property | |
649 | to memoize the return values of calls to expensive operations. A MOP | |
650 | must be be very fast to avoid making programs unusably slow, and | |
651 | memoization is able to give an appreciable speedup in many cases | |
652 | without a significant burden on memory usage.</p> | |
653 | ||
654 | ||
655 | <h4><a name="sec24" id="sec24"></a> | |
656 | Constant Shared Return Values</h4> | |
657 | ||
658 | <p class="first">Disallowing modification of values returned by protocol methods allows | |
659 | the implementation to return large data structures by reference to | |
660 | avoid expensive copying without having to do expensive data integrity | |
661 | checks or copying.</p> | |
662 | ||
663 | ||
664 | ||
665 | <h3><a name="sec25" id="sec25"></a> | |
36fbff92 | 666 | Procedural Only Where Necessary</h3> |
a7e21d41 | 667 | |
36fbff92 | 668 | <p class="first">Some operations like method invocation are inherently stateful and so |
a7e21d41 | 669 | must use a procedural protocol. There is no benefit to be gained from |
670 | using a functional protocol, and indeed an attempt would result in | |
36fbff92 | 671 | obtuse code that severely restricted the implementation. Do note that |
a7e21d41 | 672 | only a very small part of method invocation is stateful (the actual |
673 | call), and most of it can be implemented functionally (e.g. computing | |
674 | the discriminating function).</p> | |
675 | ||
676 | ||
677 | <h3><a name="sec26" id="sec26"></a> | |
2aff8b5c | 678 | Real World</h3> |
679 | ||
a7e21d41 | 680 | <h4><a name="sec27" id="sec27"></a> |
2aff8b5c | 681 | <a href="http://common-lisp.net/project/ucw/">UCW</a> and <a href="http://common-lisp.net/project/bese/arnesi.html">Arnesi</a></h4> |
682 | ||
36fbff92 | 683 | <p class="first">Arnesi uses the CLOS MOP to implement methods which are transparently |
2aff8b5c | 684 | rewritten into continuation passing style. This allows their execution |
685 | to be suspended at certain points and resumed later. UCW builds on top | |
686 | of this to support a web framework where the statelessness of http is | |
687 | hidden from the user; displaying a page suspends the execution of the | |
688 | current continuation, and resumes it upon submission. The user level | |
689 | code is completely unaware of this.</p> | |
690 | ||
691 | ||
a7e21d41 | 692 | <h4><a name="sec28" id="sec28"></a> |
2aff8b5c | 693 | <a href="http://clsql.b9.com">CLSQL</a></h4> |
694 | ||
695 | <p class="first">CLSQL uses the reflective part of the CLOS MOP to map Common Lisp data | |
696 | types into SQL types, and the intercessory protocol for slot | |
697 | allocation to map slots onto database columns or sql expressions (for | |
698 | implementing relational slots).</p> | |
699 | ||
700 | ||
a7e21d41 | 701 | <h4><a name="sec29" id="sec29"></a> |
2aff8b5c | 702 | <a href="http://common-lisp.net/project/elephant/">Elephant</a></h4> |
703 | ||
36fbff92 | 704 | <p class="first">Elephant uses the CLOS MOP to transparently store any class to disk |
a7e21d41 | 705 | and handle paging between the disk store and memory efficiently |
706 | without user intervention.</p> | |
2aff8b5c | 707 | |
708 | ||
709 | ||
710 | ||
a7e21d41 | 711 | <h2><a name="sec30" id="sec30"></a> |
36fbff92 | 712 | Sources and Further Reading</h2> |
2aff8b5c | 713 | |
a7e21d41 | 714 | <h3><a name="sec31" id="sec31"></a> |
2aff8b5c | 715 | Sources</h3> |
716 | ||
a7e21d41 | 717 | <h4><a name="sec32" id="sec32"></a> |
2aff8b5c | 718 | The Art of the Metaobject Protocol</h4> |
719 | ||
720 | <h5>Kiczales, Gregor et al. MIT Press 1991</h5> | |
721 | ||
722 | <p>Highly recommended reading even if you plan to never implement a MOP | |
723 | or use the CLOS one. The design principles it recommends are quite | |
724 | useful.</p> | |
725 | ||
726 | ||
727 | ||
a7e21d41 | 728 | <h4><a name="sec33" id="sec33"></a> |
2aff8b5c | 729 | <a href="http://www.lisp.org/mop/contents.html">CLOS MOP Specification</a></h4> |
730 | ||
731 | <p class="first">Specification of the MOP for CLOS defined in <em>The Art of the Metaobject Protocol</em>.</p> | |
732 | ||
733 | ||
a7e21d41 | 734 | <h4><a name="sec34" id="sec34"></a> |
2aff8b5c | 735 | <a href="http://citeseer.ist.psu.edu/399658.html">Metaobject Protocols: Why We Want Them and What Else They Can Do</a></h4> |
736 | ||
737 | <p class="first">A short overview of MOP design principles followed by three example | |
738 | metaobject protocols for Scheme.</p> | |
739 | ||
740 | ||
a7e21d41 | 741 | <h4><a name="sec35" id="sec35"></a> |
2aff8b5c | 742 | <a href="http://www2.parc.com/csl/groups/sda/projects/oi/towards-talk/transcript.html">Why Are Black Boxes so Hard to Reuse?</a></h4> |
743 | ||
744 | <p class="first">Transcription of a talk on the benefits of open implementations of | |
745 | software. It first discusses several problems that black box software | |
746 | implementations pose, and then presents existing solutions. It shows | |
747 | how the existing solutions are insufficient, and then provides | |
748 | metaobject protocols as a solution to most of the problems.</p> | |
749 | ||
750 | ||
751 | ||
a7e21d41 | 752 | <h3><a name="sec36" id="sec36"></a> |
2aff8b5c | 753 | Further Reading</h3> |
754 | ||
a7e21d41 | 755 | <h4><a name="sec37" id="sec37"></a> |
2aff8b5c | 756 | <a href="http://citeseer.ist.psu.edu/chiba95metaobject.html">A Metaobject Protocol for C++</a></h4> |
757 | ||
758 | <p class="first">Example of a purely compile time MOP. It implements the functionality | |
759 | of a code walker and something similar to the Lisp macro system.</p> | |
760 | ||
761 | ||
a7e21d41 | 762 | <h4><a name="sec38" id="sec38"></a> |
2aff8b5c | 763 | <a href="http://www.parc.com/csl/groups/sda/publications/papers/Kiczales-TUT95/for-web.pdf">Open Implementations and Metaobject Protocols</a></h4> |
764 | ||
765 | <p class="first">It is a bit long, but it seems to follow a similar structure to AMOP | |
766 | in introducing MOPs and their usefulness. The pages are slides with | |
767 | notes, and so the 331 pages might not actually take that long to read.</p> | |
768 | ||
769 | ||
770 | ||
36fbff92 | 771 | <h3><a name="sec39" id="sec39"></a> |
772 | Software</h3> | |
773 | ||
774 | <h4><a name="sec40" id="sec40"></a> | |
775 | <a href="http://common-lisp.net/project/closer/closer-mop.html">Closer to MOP</a></h4> | |
776 | ||
777 | <p class="first">Compatibility layer that attempts to present the <em>Art of the Metaobject | |
778 | Protocol</em> MOP specification properly in as many Common Lisp | |
779 | implementation as possible.</p> | |
780 | ||
781 | ||
782 | ||
2aff8b5c | 783 | |
784 | <!-- Page published by Emacs Muse ends here --> | |
785 | ||
786 | <p class="cke-buttons"> | |
787 | <!-- validating badges, any browser, etc --> | |
98266870 CE |
788 | <a href="https://validator.w3.org/check/referer"><img |
789 | src="https://www.w3.org/Icons/valid-xhtml10" | |
2aff8b5c | 790 | alt="Valid XHTML 1.0!" /></a> |
791 | ||
98266870 | 792 | <a href="https://www.anybrowser.org/campaign/"><img |
2aff8b5c | 793 | src="img/buttons/w3c_ab.png" alt="[ Viewable With Any Browser |
794 | ]" /></a> | |
795 | ||
98266870 | 796 | <a href="https://www.debian.org/"><img |
2aff8b5c | 797 | src="img/buttons/debian.png" alt="[ Powered by Debian ]" /></a> |
798 | ||
98266870 | 799 | <a href="https://hcoop.net/"> |
2aff8b5c | 800 | <img src="img/buttons/hcoop.png" |
801 | alt="[ Hosted by HCoop]" /> | |
802 | </a> | |
803 | ||
98266870 | 804 | <a href="https://www.fsf.org/register_form?referrer=114"> |
2aff8b5c | 805 | <img src="img/buttons/fsf_member.png" |
806 | alt="[ FSF Associate Member ]" /> | |
807 | </a> | |
808 | </p> | |
809 | ||
11f9bd69 CE |
810 | <p class="cke-footer"><ascii_phil> There once was a man named Bertold |
811 | <ascii_phil> Who drank beer when the weather grew cold | |
812 | <ascii_phil> As he reached for his cup... | |
813 | <ascii_phil> "NEEEEVER GONNA GIVE YOU UP!!!" | |
814 | <ascii_phil> Oh, snap! You just got limerickrolled! | |
2aff8b5c | 815 | </p> |
816 | <p class="cke-timestamp">Last Modified: | |
f6d19803 | 817 | January 21, 2013</p> |
2aff8b5c | 818 | </body> |
819 | </html> |